package com.sky.aspect;

import com.sky.annotation.AutoFill;
import com.sky.constant.AutoFillConstant;
import com.sky.context.BaseContext;
import com.sky.enumeration.OperationType;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.time.LocalDateTime;

/**
 * 自定义切面，实现公共字段自动填充
 */
@Aspect
@Component
@Slf4j
public class AutoFillAspect {
    /**
     * 切入点
     */
    //功能：定义一个切点表达式，用于匹配特定的方法。
    @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
    //功能：定义一个空方法，作为切点的标识。
    //解释：AutoFillPointcut()：这是一个空方法，仅用于作为切点的标识。切点的名称是AutoFillPointcut，可以在其他地方引用。
    //{}：方法体为空，因为切点本身不包含任何逻辑，只是用于定义匹配规则。
    public void AutoFillPointcut() {}

    /**
     * 前置通知,在通知中进行公共字段的赋值
     * @param joinPoint
     */
    @Before("AutoFillPointcut()")
    public void afterFill(JoinPoint joinPoint) {
        log.info("开始进行公共字段自动填充...");
        try{
            //功能：获取切点（joinPoint）处方法的参数列表。
            //解释：joinPoint是AOP中的一个概念，表示当前正在执行的方法的连接点。getArgs()方法返回一个Object数组，包含调用该方法时传递的所有参数。
            Object[] args = joinPoint.getArgs();
            //功能：获取当前方法的签名。
            //解释：getSignature()方法返回一个Signature对象，表示当前方法的签名。这里将其转换为MethodSignature，以便获取方法的具体信息（如方法名、参数类型等）。
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            //功能：获取当前方法上的@AutoFill注解。
            //解释：getMethod()返回当前方法对应的Method对象，getAnnotation(AutoFill.class)尝试获取该方法上的@AutoFill注解实例。
            AutoFill autoFill = methodSignature.getMethod().getAnnotation(AutoFill.class);
            //功能：从@AutoFill注解中提取OperationType的值。
            //解释：value()是@AutoFill注解的一个属性，表示当前操作类型（如INSERT或UPDATE）。
            OperationType operationType = autoFill.value();
            if(args == null || args.length == 0){
                return;
            }
            //功能：获取方法的第一个参数。
            //解释：假设第一个参数是需要自动填充字段的实体对象。
            Object entity = args[0];
            //功能：获取实体对象的类对象。
            //解释：getClass()返回实体对象的运行时类。
            Class<?> clazz = entity.getClass();
            //功能：通过反射获取实体类的setUpdateTime方法。
            //解释：getDeclaredMethod根据方法名和参数类型查找方法。AutoFillConstant.SET_UPDATE_TIME可能是常量，表示方法名（如setUpdateTime）。
            Method setUpdateTime = clazz.getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
            //功能：通过反射获取实体类的setUpdateUser方法。
            //解释：类似于setUpdateTime，这里获取setUpdateUser方法。
            Method setUpdateUser = clazz.getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
            //功能：调用setUpdateTime方法，设置当前时间为更新时间。
            //解释：invoke方法用于调用实体对象的setUpdateTime方法，传入当前时间LocalDateTime.now()。
            setUpdateTime.invoke(entity,LocalDateTime.now());
            //功能：调用setUpdateUser方法，设置当前用户ID为更新用户。
            //解释：BaseContext.getCurrentId()可能是获取当前用户ID的工具方法。
            setUpdateUser.invoke(entity,BaseContext.getCurrentId());
            //功能：检查操作类型是否为INSERT。
            //解释：如果是插入操作，需要额外设置创建时间和创建用户。
            if(operationType == OperationType.INSERT){
                //功能：通过反射获取实体类的setCreateTime方法。
                //解释：类似于setUpdateTime，这里获取setCreateTime方法。
                Method setCreateTime = clazz.getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
                //功能：通过反射获取实体类的setCreateUser方法。
                //解释：类似于setUpdateUser，这里获取setCreateUser方法。
                Method setCreateUser = clazz.getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
                //功能：调用setCreateTime方法，设置当前时间为创建时间。
                //解释：仅在插入操作时设置创建时间。
                setCreateTime.invoke(entity,LocalDateTime.now());
                //功能：调用setCreateUser方法，设置当前用户ID为创建用户。
                //解释：仅在插入操作时设置创建用户。
                setCreateUser.invoke(entity,BaseContext.getCurrentId());
            }
        }catch (Exception e){
            log.info("填充失败：{}",e.getMessage());
        }
    }

}
